﻿/*
 * Stardust Software R&D Group Confidential
 * Project: Vesalius Image Annotation - Name as Salomay
 *
 * (C) Copyright Stardust Software R&D Group. 2013
 *
 * The source code for this program is not published or otherwise divested of 
 * its trade secrets
 */

/**
 *	Array iterator
 * @author:	Cheng Li, Sep 06, 2013
 **/
var $A = Array.from = function (iterable) {
    if (!iterable) return [];
    if (iterable.toArray) {
        return iterable.toArray();
    } else {
        var results = [];
        for (var i = 0; i < iterable.length; i++)
            results.push(iterable[i]);
        return results;
    }
}

/**
 * Standard class declaration 
 * @author:	Cheng Li, Sep 06, 2013
 **/
var Class = {
    create: function () {
        return function () {
            this.initialize.apply(this, arguments);
        }
    }
}

/**
 * Standard class inherit 
 * @author:	Cheng Li, Sep 06, 2013
 **/
Class.extend = function (destination, source) {
    for (property in source) {
        if (property.toString().indexOf("super_") != -1) continue;
        if (destination[property] != null) {
            destination["super_" + property] = source[property];
            continue;
        } else {
            destination[property] = source[property];
        }
    }
    return destination;
}

/**
 * Bind scope 
 * @author:	Cheng Li, Sep 06, 2013
 **/
Function.prototype.bind = function () {
    var __method = this, args = $A(arguments), object = args.shift();
    return function () {
        return __method.apply(object, args.concat($A(arguments)));
    }
}

/**
 * Bind scope with event argument
 * @author:	Cheng Li, Sep 06, 2013
 **/
Function.prototype.bindAsEventListener = function () {
    var __method = this, args = $A(arguments), object = args.shift();
    return function (event) {
        return __method.apply(object, [event || window.event].concat(args));
    }
}

/**
 * Observer class, the instance of this class contains two members, in one word, they are <who> does <what> when observing event fired
 * @author:	Cheng Li, Sep 06, 2013
 **/
var Observer = Class.create();
Observer.prototype = {	
   /**
    * Constructor
	**/
   initialize: function(executer, method){
       this.executer = executer;
       this.method = method;
   },
	
   /**
    * Execute the callback when event fired
	**/
   Notify: function() {
		return this.method.apply(this.executer,arguments);
   },
   
   /**
    * Check if the current instance is equal to given observer
	**/
   IsEqual:function(observer) {
		return this.executer.IsEqual(observer.executer) && this.method == observer.method;
   }
}

/**
 * Observer chain manages all observers who care the same event
 * @author:	Cheng Li, Sep 06, 2013
 **/
var ObserverChain = Class.create();
ObserverChain.prototype = {
   /**
    * Constructor
	**/
   initialize: function(event_name){
       this.event_name = name;
       this.observer_list = [];
   },
   
   /**
    * Add new observer to chain
	* @param observer the new observer
	**/
   AddObserver:function(observer){
      this.observer_list.push(observer);
   },
   
   /**
    * Notify all observers on chain when event fired
	* @param context data context
	**/
   NotifyAllObservers: function(context) {
      for(n=0;n<this.observer_list.length;n++){
         try{
             this.observer_list[n].Notify(context);
         }catch(e){
         }
      }  
   },
   
   /**
    * Remove observer from event chain
	* @param observer observer going to be removed
	**/
   StopObserving: function(observer) {
	    for(n=0;n<this.observer_list.length;n++){
         try{
             if (this.observer_list[n].IsEqual(observer)) {
				this.observer_list.splice(n,1);
			 }
         }catch(e){
         }
      } 
   }
}

/**
 * Observer manager manages all event chains for each event. When one event fired, the om will notify all observers in the related event chain
 * @author:	Cheng Li, Sep 06, 2013
 **/
var ObserverManager = Class.create();
ObserverManager.prototype = {
	/**
	 * Constructor
	 **/
    initialize: function(){
      this.event_list = [];
    },
    
	/**
	 * Add an observer for given event
	 * @param event_name name of event
	 * @observer observer who listen the event
	 **/
    AddObserver:function(event_name,observer){
      if(this.event_list.length<=0||this.event_list[event_name]==null){
        this.event_list.push(event_name);
        this.event_list[event_name] = new ObserverChain(event_name);
      }
      this.event_list[event_name].AddObserver(observer);	
    },
	
	/**
	 * Tell all observers the given event has been fired
	 * @param event_name name of event
	 * @context data context
	 **/
    NotifyAllObservers: function(event_name,context) {
       if(this.event_list[event_name]){
       	    return this.event_list[event_name].NotifyAllObservers(context);
       }
    },
	
	/**
	 * Remove observer from event chain according to event name
	 * @param event_name name of event
	 * @observer Removed observer
	 **/
    StopObserving: function(event_name, observer) {
		if(this.event_list[event_name]){
       	    this.event_list[event_name].StopObserving(observer);
       }
    }
}

 /**
  * Information class allows you to output all kinds of message to developers or log(error, warning, debugInfo)
  * @author Cheng Li, Sep 06, 2013
  **/
var Information = {
    error: function (message) {
        alert(message);
    },

    warning: function (message) {
       // alert(message);
    },

    debugInfo: function (message) {
      //  alert(message);
    }
}

/**
 * Common function helper class
 * @author:	Cheng Li, Sep 06, 2013
 **/
var Utils = {
	/**
	 * Get element absolute offset left and offset top
	 * @author:	Cheng Li, Sep 06, 2013
	 * @param element
	 * @return JSON value include X and Y coordinates {x:1,y:22}
	 **/
	getElementLocationInViewPort: function(webElement) {
		var x = 0;
		var y = 0;
		// Convert JQuery object to DOM node
		try {
			webElement = webElement[0];
		} catch (e) {}
		var element = webElement;
		while (element) { // the loop to calculate element offsets in view port
			if (element == webElement.ownerDocument.body) break;
			x+= element.offsetLeft + element.offsetParent.clientLeft;
			y+= element.offsetTop + element.offsetParent.clientTop;
			element = element.offsetParent;
		}
		element = webElement;
		var s_x = 0;
		var s_y = 0;
		while (element) { // the loop to calculate total scroll distance
			s_x += element.scrollLeft || 0;
			s_y += element.scrollTop || 0;
			element = element.parentNode;
		}
		return {x:(x-s_x), y:(y-s_y)};
	}
}

/**
 * Runtime context data storage
 * @author Cheng Li, Sep 08,2013
 **/
var Context = Class.create();
Context.prototype = {
  initialize: function(){
     this.param_list = [];   
     this.id = "";    
  },
  
  get:function(param_name){
    var result = "";
    if(this.param_list[param_name]){
      result = this.param_list[param_name];
    }
    return result;
  },

  set:function(param_name,param_value){
     if(this.param_list.length<=0||this.param_list[param_name]==null){
        this.param_list.push(param_name);
     }
     this.param_list[param_name] = param_value;
  },
  
  get$:function(property_name){
     var result = ""; 
     try{
        eval ('result = this.'+property_name);
     }catch(e){}
     return result;
  },

  set$:function(property_name,property_value){
     try{
        eval ('this.'+property_name+" = "+property_value);
     }catch(e){}
  }
}